package io.github.talelin.latticy.service.impl;

import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.github.talelin.latticy.bo.InboundOrderSkuVo;
import io.github.talelin.latticy.bo.LocationByAreaVo;
import io.github.talelin.latticy.common.LocalUser;
import io.github.talelin.latticy.dto.feign.InboundOrderOpenFeignDTO;
import io.github.talelin.latticy.dto.putaway.PutAwayDTO;
import io.github.talelin.latticy.dto.putaway.PutAwayPageDTO;
import io.github.talelin.latticy.dto.putaway.PutAwaySkuDTO;
import io.github.talelin.latticy.dto.stock.BatchStockDTO;
import io.github.talelin.latticy.dto.stock.BatchStockVO;
import io.github.talelin.latticy.mapper.LocationMapper;
import io.github.talelin.latticy.mapper.PutAwayMapper;
import io.github.talelin.latticy.model.BatchStockDO;
import io.github.talelin.latticy.model.LocationDO;
import io.github.talelin.latticy.model.PutAwayDO;
import io.github.talelin.latticy.model.UserDO;
import io.github.talelin.latticy.service.*;
import io.github.talelin.latticy.service.feign.InboundOrderFeignService;
import io.github.talelin.latticy.util.PageHelpUtil;
import io.github.talelin.latticy.vo.PutAwayVO;
import io.github.talelin.latticy.vo.ReceivingRecordVO;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;

/**
 * <p>
 * 上架表 服务实现类
 * </p>
 *
 * @author generator@wulongbo
 * @since 2022-06-29
 */
@Service
public class PutAwayServiceImpl extends ServiceImpl<PutAwayMapper, PutAwayDO> implements PutAwayService {

    @Autowired
    private LocationService locationService;

    @Autowired
    private BatchStockService batchStockService;

    @Autowired
    private IdGeneratorService idGeneratorService;

    @Autowired
    private InboundOrderFeignService inboundOrderFeignService;

    @Autowired
    private ReceivingRecordService receivingRecordService;


    private BiPredicate<Integer, Integer> isDepositEnough = (totalPutAwayQuantity, receivingTotalCount) -> totalPutAwayQuantity.compareTo(receivingTotalCount) == 0;

    @Override
    @Transactional
    public void putAwayInfo(PutAwayDTO putAwayDto) {
        //1.判断入库单上架数量=收货数量则说明上架完成
        ReceivingRecordVO receivingRecordList = receivingRecordService.getReceivingRecordList(putAwayDto.getInboundOrderNo());

        Integer receivingTotalCount = receivingRecordList.getReceivingPackages().parallelStream()
                .map(packages -> packages.getRecordItems().parallelStream().map(recordItems -> recordItems.getDamaged() +
                        recordItems.getSpecimen()).reduce(0, Integer::sum)).reduce(0, Integer::sum);

        PutAwayPageDTO putAwayDTO=new PutAwayPageDTO();
        BeanUtils.copyProperties(putAwayDto,putAwayDTO);
        List<PutAwayDO> putAwayList = this.getPutAwayList(putAwayDTO);
        Integer thisPutAwayQuantity = putAwayDto.getPutAwaySkuList().parallelStream().map(PutAwaySkuDTO::getQuantity).reduce(0, Integer::sum);
        Integer putAwayQuantity = putAwayList.parallelStream().map(PutAwayDO::getQuantity).reduce(0, Integer::sum);
        Assert.isTrue(!isDepositEnough.test(putAwayQuantity,receivingTotalCount),"18518");
        if(isDepositEnough.test(thisPutAwayQuantity+putAwayQuantity,receivingTotalCount)){
            InboundOrderOpenFeignDTO orderOpenFeignDTO = new InboundOrderOpenFeignDTO();
            BeanUtils.copyProperties(putAwayDto,orderOpenFeignDTO);
            inboundOrderFeignService.inboundOrderPutAway(orderOpenFeignDTO);
        }


        List<PutAwaySkuDTO> putAwaySkuList = putAwayDto.getPutAwaySkuList();
        List<Integer> locationIdList = putAwaySkuList.parallelStream().map(PutAwaySkuDTO::getLocationId).distinct().collect(Collectors.toList());
        List<LocationByAreaVo> locationByIdList = locationService.getLocationByIdList(locationIdList);
        Assert.notNull(locationByIdList, "18502");
        UserDO userDO = LocalUser.getLocalUser();
        //1.上架对应数据,调用批次库存方法
        Map<Integer, String> locationMap = locationByIdList.parallelStream().collect(Collectors.toMap(LocationDO::getId, LocationDO::getName));
        List<PutAwayDO> awayDOList = putAwaySkuList.parallelStream().map(putAway -> {
            PutAwayDO putAwayDO = new PutAwayDO();
            BeanUtils.copyProperties(putAway, putAwayDO);
            putAwayDO.setWarehouseCode(putAwayDto.getWarehouseCode());
            putAwayDO.setTenant(putAwayDto.getTenant());
            putAwayDO.setInboundOrderNo(putAwayDto.getInboundOrderNo());
            String batchNo = idGeneratorService.generatorId("PA");
            putAwayDO.setBatchNo(batchNo);
            BatchStockDO batchStockDO = new BatchStockDO();
            BeanUtils.copyProperties(putAway, batchStockDO);
            batchStockDO.setOrderNo(putAwayDto.getInboundOrderNo());
            batchStockDO.setCustomerCode(putAwayDto.getTenant());
            batchStockDO.setLocation(MapUtils.getString(locationMap, putAway.getLocationId()));
            batchStockDO.setLocationId(putAway.getLocationId());
            batchStockDO.setContainerNo(putAway.getPackageNo());
            batchStockDO.setWarehouseCode(putAwayDto.getWarehouseCode());
            batchStockDO.setCreateTime(new Date());
            batchStockDO.setLibraryTime(new Date());
            batchStockDO.setQuantity(Long.valueOf(putAway.getQuantity()));
            batchStockDO.setPutAwayBatchNo(batchNo);
            batchStockService.batchStockSave(batchStockDO);
            putAwayDO.setPutAwayBy(userDO.getUsername());
            return putAwayDO;
        }).collect(Collectors.toList());
        this.saveBatch(awayDOList);
    }

    @Override
    public List<String> getSkuByLocationId(List<Integer> ids) {
        // 查询区域下所有的sku.
        List<LocationDO> locationDOS = locationService.getLocationAreaList(ids);
        if (CollectionUtils.isEmpty(locationDOS)) {
            return null;
        }
        List<Integer> locationIds = locationDOS.stream().map(LocationDO::getId).distinct().collect(Collectors.toList());
        LambdaQueryWrapper<PutAwayDO> qw2 = Wrappers.lambdaQuery(PutAwayDO.class)
                .in(PutAwayDO::getLocationId, locationIds);
        List<PutAwayDO> PutAwayDOS = this.baseMapper.selectList(qw2);
        if (CollectionUtils.isEmpty(PutAwayDOS)) {
            return null;
        }
        return PutAwayDOS.stream().map(PutAwayDO::getSku).distinct().collect(Collectors.toList());
    }

    @Override
    public IPage<PutAwayVO> getPutAwayPage(PutAwayPageDTO putAwayDTO) {
        Page<PutAwayDO> putAwayDOPage = this.baseMapper.selectPage(new Page<>(putAwayDTO.getCurrPage(), putAwayDTO.getPageSize()), wrapper(putAwayDTO));
        IPage<PutAwayVO> putAwayVOPage = PageHelpUtil.convertPage(putAwayDOPage, PutAwayVO.class);
        List<Integer> locationIds= putAwayDOPage.getRecords().parallelStream().map(PutAwayDO::getLocationId).distinct().collect(Collectors.toList());
        if(CollectionUtils.isNotEmpty(locationIds)) {
            List<LocationByAreaVo> locationByIdList = locationService.getLocationByIdList(locationIds);
            Assert.notNull(locationByIdList, "18502");
            Map<Integer, LocationDO> locationMap = locationByIdList.parallelStream().collect(Collectors.toMap(LocationDO::getId, location -> location));
            if (MapUtils.isNotEmpty(locationMap)) {
                putAwayVOPage.getRecords().forEach(putAway -> {
                    putAway.setLocationName(locationMap.get(putAway.getLocationId()).getName());
                });
            }
        }

        List<String> skuList = putAwayDOPage.getRecords().parallelStream().map(PutAwayDO::getSku).distinct().collect(Collectors.toList());
        List<BatchStockDO> batchStockList = batchStockService.list(new LambdaQueryWrapper<BatchStockDO>().eq(StrUtil.isNotEmpty(putAwayDTO.getWarehouseCode()), BatchStockDO::getWarehouseCode,
                putAwayDTO.getWarehouseCode()).eq(StrUtil.isNotEmpty(putAwayDTO.getInboundOrderNo()), BatchStockDO::getOrderNo,
                putAwayDTO.getInboundOrderNo()).in(CollectionUtils.isNotEmpty(skuList), BatchStockDO::getSku, skuList));
        Date date = new Date();
        Map<String, BatchStockDO> stockDOMap = batchStockList.parallelStream().collect(Collectors.toMap(BatchStockDO::getPutAwayBatchNo, batchStockDO -> batchStockDO, (k1, k2) -> k1));
        if(MapUtils.isNotEmpty(stockDOMap)){
            putAwayVOPage.getRecords().forEach(putAway->{
                BatchStockDO stockDO = stockDOMap.get(putAway.getBatchNo());
                if(stockDO!=null){
                    //库龄计算
                    String stockAge = DateUtil.formatBetween(stockDO.getLibraryTime(), date, BetweenFormatter.Level.DAY);
                    putAway.setStockAge(stockAge);
                }
            });
        }
        return putAwayVOPage;
    }

    @Override
    public List<PutAwayDO> getPutAwayList(PutAwayPageDTO putAwayDTO) {
        return this.list(wrapper(putAwayDTO));
    }

    @Override
    public void resizeLibraryAge(PutAwaySkuDTO putAwayDTO) {
        PutAwayDO putAway = this.getById(putAwayDTO.getPutAwayId());
        Assert.notNull(putAway,"18712");
        BatchStockDTO batchStockDO=new BatchStockDTO();
        batchStockDO.setPutAwayBatchNo(putAway.getBatchNo());
        batchStockDO.setOrderNo(putAway.getInboundOrderNo());
        batchStockDO.setCustomerCode(putAway.getTenant());
        batchStockDO.setWarehouseCode(putAway.getWarehouseCode());
        batchStockDO.setSku(putAway.getSku());
        batchStockDO.setLibraryTime(DateUtil.parse(putAwayDTO.getLibraryTime()));
        batchStockService.resizeLibraryAge(batchStockDO);
    }


    @Override
    public void resizeStockNum(PutAwaySkuDTO putAwayDTO) {
        PutAwayDO putAway = this.getById(putAwayDTO.getPutAwayId());
        Assert.notNull(putAway,"18712");
        PutAwayDO putAwayDO = new PutAwayDO();
        putAwayDO.setId(putAway.getId());
        putAwayDO.setQuantity(putAwayDTO.getQuantity());
        putAwayDO.setOtherRemark(putAwayDTO.getOtherRemark());
        Assert.isTrue(this.updateById(putAwayDO),"18713");
        BatchStockDTO batchStockDO=new BatchStockDTO();
        batchStockDO.setPutAwayBatchNo(putAway.getBatchNo());
        batchStockDO.setOrderNo(putAway.getInboundOrderNo());
        batchStockDO.setCustomerCode(putAway.getTenant());
        batchStockDO.setWarehouseCode(putAway.getWarehouseCode());
        batchStockDO.setSku(putAway.getSku());
        batchStockDO.setQuantity(Long.valueOf(putAwayDTO.getQuantity()));
        batchStockService.resizeStockNum(batchStockDO);
    }

    @Override
    @Transactional
    public void moveLocation(PutAwaySkuDTO putAwayDTO) {
        PutAwayDO putAway = this.getById(putAwayDTO.getPutAwayId());
        Assert.notNull(putAway,"18712");
        Assert.isFalse(putAwayDTO.getQuantity()>putAway.getQuantity(),"18714");
        PutAwayDO putAwayDO = new PutAwayDO();
        putAwayDO.setId(putAway.getId());
        putAwayDO.setQuantity(putAway.getQuantity()-putAwayDTO.getQuantity());
        Assert.isTrue(this.updateById(putAwayDO),"18713");
        BeanUtils.copyProperties(putAway,putAwayDO);
        String batchNo = idGeneratorService.generatorId("PA");
        putAwayDO.setBatchNo(batchNo);
        putAwayDO.setQuantity(putAwayDTO.getQuantity());
        putAwayDO.setLocationId(putAwayDTO.getLocationId());
        putAwayDO.setOtherRemark(putAwayDTO.getOtherRemark());
        this.save(putAwayDO);
    }



    private QueryWrapper<PutAwayDO> wrapper(PutAwayPageDTO putAwayDTO){
        QueryWrapper<PutAwayDO> wrapper = new QueryWrapper<>();
            wrapper.lambda()
                .like(StringUtils.isNotBlank(putAwayDTO.getPackageNo()), PutAwayDO::getPackageNo, putAwayDTO.getPackageNo())
                .like(StringUtils.isNotBlank(putAwayDTO.getSku()), PutAwayDO::getSku, putAwayDTO.getSku())
                .in(CollectionUtils.isNotEmpty(putAwayDTO.getSkus()), PutAwayDO::getSku, putAwayDTO.getSkus())
                .eq(StringUtils.isNotBlank(putAwayDTO.getWarehouseCode()), PutAwayDO::getWarehouseCode, putAwayDTO.getWarehouseCode())
                .eq(putAwayDTO.getLocationId()!=null, PutAwayDO::getLocationId, putAwayDTO.getLocationId())
                .eq(StringUtils.isNotBlank(putAwayDTO.getInboundOrderNo()), PutAwayDO::getInboundOrderNo, putAwayDTO.getInboundOrderNo())
                    .between(putAwayDTO.getStartTime() != null && putAwayDTO.getEndTime() != null, PutAwayDO::getCreateTime, putAwayDTO.getStartTime(),putAwayDTO.getEndTime());
        return wrapper;
    }
}
